/*
 * systrace - Should be a tool to analyze system call traces but can be used
 *            to analyze sequences of generic strings
 *
 * Copyright (c) 2011 Matteo Cicuttin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <vector>
#include <sstream>

#include "EnterExitAnalysis.h"

EnterExitAnalysis::EnterExitAnalysis()
{
	set_module_name("enterexit");
	_stkmax = 0;
	
	// read these from file!!
	add_matched_symbols("open_nocancel", "close_nocancel");
	add_matched_symbols("open", "close");
	add_matched_symbols("socket", "close");
	add_matched_symbols("accept", "close");
	add_matched_symbols("mmap", "munmap");
}

void
EnterExitAnalysis::print_result(ostream& os)
{
	map<string, int>::iterator itor;
	
	os << "Total returns not matching calls at the same level:" << endl;
	for(itor = _bad_returns.begin(); itor != _bad_returns.end(); itor++)
		os << "\x1B[1;31m" << (*itor).first << ":\x1B[0m " << (*itor).second << endl;
	
	os << "Pending stack: " << _opstack.size() << " levels, "
	   << "max depth reached: " << _stkmax << endl;
}

void
EnterExitAnalysis::badret(const string& call)
{
	if (_bad_returns.find(call) == _bad_returns.end())
		_bad_returns[call] = 1;
	else
		_bad_returns[call]++;
}

void
EnterExitAnalysis::add_sample(const string& s)
{
	int stkdepth;
	/* s is in _callretmap: we are entering a state */
	if ( _callretmap.find(s) != _callretmap.end() )
	{
		_opstack.push(s);
		stkdepth = _opstack.size();
		_stkmax = _stkmax > stkdepth ? _stkmax : stkdepth;
		_stack_depth_hist.push_back( stkdepth );
	}
	
	/* s is in _retcallmap: we are exiting a state */
	set<string>::iterator itor;
	if ( _retcallmap.find(s) != _retcallmap.end() )
	{
		if ( _opstack.empty() )
		{
			badret(s);
			return;
		}
		
		itor = _retcallmap[s].find( _opstack.top() );
		if ( itor == _retcallmap[s].end() )
			badret(s);
		
		_opstack.pop();
		_stack_depth_hist.push_back( _opstack.size() );
	}
}

void
EnterExitAnalysis::add_matched_symbols(const string& call, const string& ret)
{
	_callretmap[call].insert(ret);
	_retcallmap[ret].insert(call);
}

void
EnterExitAnalysis::print_sets(void)
{
	map<string, set<string> >::iterator map_itor;
	cout << "\x1B[1mCall symbols and corresponding returns:\x1B[0m" << endl;
	for(map_itor = _callretmap.begin(); map_itor != _callretmap.end(); map_itor++)
	{
		set<string>::iterator set_itor;
		set<string>& s = (*map_itor).second;
		cout << "\x1B[1;31m" << (*map_itor).first << ":\x1B[0m ";
		for(set_itor = s.begin(); set_itor != s.end(); set_itor++)
			cout << (*set_itor) << " ";
		cout << endl;
	}
	
	cout << "\x1B[1mReturn symbols and corresponding calls:\x1B[0m" << endl;
	for(map_itor = _retcallmap.begin(); map_itor != _retcallmap.end(); map_itor++)
	{
		set<string>::iterator set_itor;
		set<string>& s = (*map_itor).second;
		cout << "\x1B[1;31m" << (*map_itor).first << ":\x1B[0m ";
		for(set_itor = s.begin(); set_itor != s.end(); set_itor++)
			cout << (*set_itor) << " ";
		cout << endl;
	}
}

void
EnterExitAnalysis::help(void)
{
	cout << "  \x1B[1mprint [name]\x1B[0m         print results" << endl;
	cout << "  \x1B[1mprintsets\x1B[0m            print matched pairs" << endl;
	cout << "  \x1B[1minsertpairs <p1> <p2>\x1B[0minsert a new matched pair" << endl;
	cout << "  \x1B[1menable\x1B[0m               enable module" << endl;
	cout << "  \x1B[1mdisable\x1B[0m              disable module" << endl;
	cout << "  \x1B[1mexit\x1B[0m                 return to parent context" << endl;
	cout << "  \x1B[1mflush\x1B[0m                flush data structures" << endl;
}

void
EnterExitAnalysis::run_interactive(void)
{
	while (1)
	{
		stringstream oss;
		vector<string> tokens;
		char *s, *pch;
		
		oss << "\x1B[1;33m" << module_name() << " >>\x1B[0m ";
		
		s = readline(oss.str().c_str());
		add_history(s);
		
		pch = strtok(s, " ");
		while (pch)
		{
			tokens.push_back(pch);
			pch = strtok(NULL, " ");
		}
		
		if ( tokens.size() == 0 )
			continue;
		
		if ( tokens.at(0) == "help" )
			help();
	
		if ( tokens.at(0) == "insertpair" )
		{
			if (tokens.size() != 3)
			{
				cout << "Specify the two matched symbols" << endl;
				continue;
			}
			add_matched_symbols(tokens.at(1), tokens.at(2));
		}
		
		if ( tokens.at(0) == "printsets" )
			print_sets();
		
		if ( tokens.at(0) == "print" )
			print_result(cout);
		
		if ( tokens.at(0) == "enable" )
			set_enabled(true);
			
		if ( tokens.at(0) == "disable" )
			set_enabled(false);
			
		if ( tokens.at(0) == "exit" )
			break;
			
		if ( tokens.at(0) == "flush" )
			flush();
		
		tokens.clear();
		free(s);
	
	}
}

void
EnterExitAnalysis::flush(void)
{
	//_callretmap.clear();
	//_retcallmap.clear();
	while (!_opstack.empty())
		_opstack.pop();
	_bad_returns.clear();
	_stkmax = 0;
}


